﻿using Gadgeteer.Interfaces;

namespace Gadgeteer.Modules.SolderMonkey
{
    /// <summary>
    ///     A Encoder module for Microsoft .NET Gadgeteer
    /// </summary>
    public class Encoder : Module
    {
        /// <summary>
        ///     Represents the delegate that is used to handle the <see cref="!:PositiveStep" />
        ///     and <see cref="!:NegativeStep" /> events.
        /// </summary>
        /// <param name="sender">The <see cref="T:Gadgeteer.Modules.SolderMonkey.Encoder" /> object that raised the event.</param>
        /// <param name="dir">The state of the button of the <see cref="T:Gadgeteer.Modules.SolderMonkey.Encoder" /></param>
        public delegate void StepEventHandler(Encoder sender, StepDirection dir);

        /// <summary>
        ///     Represents the direction of rotation for the most recent Step event
        ///     <see cref="T:Gadgeteer.Modules.SolderMonkey.Encoder" />.
        /// </summary>
        public enum StepDirection
        {
            Pos,
            Neg,
        }

        private readonly DigitalInput buttonPin;
        private readonly DigitalInput dirPin;
        private StepEventHandler onStep;
        private InterruptInput stepPin;

        /// <summary />
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        /// <param name="socketNumberTwo">The second socket that this module is plugged in to.</param>
        public Encoder(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);
            stepPin = new InterruptInput(socket, Socket.Pin.Three, GlitchFilterMode.On, ResistorMode.PullUp,
                InterruptMode.RisingEdge, this);
            dirPin = new DigitalInput(socket, Socket.Pin.Five, GlitchFilterMode.On, ResistorMode.PullUp, this);
            buttonPin = new DigitalInput(socket, Socket.Pin.Four, GlitchFilterMode.On, ResistorMode.PullUp, this);
            stepPin.Interrupt += _input_Interrupt;
        }

        /// <summary>
        ///     Gets a value that indicates whether the button of the Encoder is pressed.
        /// </summary>
        public bool IsPressed
        {
            get { return buttonPin.Read(); }
        }

        /// <summary>
        ///     Raised when the shaft of the <see cref="T:Gadgeteer.Modules.SolderMonkey.Encoder" /> is rotated.
        /// </summary>
        /// <remarks>
        ///     Implement this event handler and/or the <see cref="!:NegativeStep" /> event handler
        ///     when you want to provide an action associated with step events.
        /// </remarks>
        public event StepEventHandler Step;

        private void _input_Interrupt(InterruptInput input, bool value)
        {
            OnStepEvent(this, dirPin.Read() ? StepDirection.Pos : StepDirection.Neg);
        }

        /// <summary>
        ///     Raises the <see cref="!:PositiveStep" /> or <see cref="!:NegativeStep" /> event.
        /// </summary>
        /// <param name="sender">The <see cref="T:Gadgeteer.Modules.SolderMonkey.Encoder" /> that raised the event.</param>
        /// <param name="stepDir">The state of the button.</param>
        protected virtual void OnStepEvent(Encoder sender, StepDirection stepDir)
        {
            if (onStep == null)
                onStep = OnStepEvent;
            if (!Program.CheckAndInvoke(Step, onStep, (object) sender, (object) stepDir))
                return;
            Step(sender, stepDir);
        }
    }
}